package models // 注意包名必须是 models

import (
	"crypto/md5"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/beego/beego/v2/server/web/context"

	"github.com/beego/beego/v2/core/logs"
	"github.com/google/uuid"
)

// 文件重命名操作（注意首字母大写才能被外部访问）
func RenameFile(oldPath string, newName string) error {
	// 获取目标目录
	dir := filepath.Dir(oldPath)
	// 构建新路径
	newPath := filepath.Join(dir, newName)

	// 执行重命名
	if err := os.Rename(oldPath, newPath); err != nil {
		logs.Error("rename failed, oldpath:%s, newpath:%s,  err: %s", oldPath, newPath, err.Error())
		return err
	}
	return nil
}

// 计算文件 MD5 的通用方法
func CalculateFileMD5(filePath string) (string, error) {
	//filePath = "FileList/fa-solid-900.ttf"
	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		logs.Error("open file failed, path: %s", filePath)
		return "", err
	}
	defer file.Close()

	// 创建 MD5 计算器
	hash := md5.New()

	// 流式处理大文件（避免内存溢出）
	if _, err := io.Copy(hash, file); err != nil {
		logs.Error("copy failed, path: %s", filePath)
		return "", err
	}

	// 生成最终哈希值
	hashInBytes := hash.Sum(nil)[:16]
	return hex.EncodeToString(hashInBytes), nil
}

// 校验文件完整性
func VerifyFileMD5(filePath, expectedMD5 string) bool {
	actualMD5, err := CalculateFileMD5(filePath)
	if err != nil {
		return false
	}
	return actualMD5 == expectedMD5
}

func GetMD5Conf() map[string]string {
	// 1. 确定缓存文件路径
	cachePath := "md5_cache.json"
	md5Cache := make(map[string]string)

	// 2. 尝试读取缓存文件
	if data, err := os.ReadFile(cachePath); err == nil && len(data) != 0 {
		if err := json.Unmarshal(data, &md5Cache); err != nil {
			logs.Error("解析MD5缓存失败: %v", err)
		}
	}

	return md5Cache
}

// 使用结构体封装保证线程安全
type SafeCache struct {
	data map[string]string
	mu   sync.RWMutex
}

var GlobalCacheMgr = SafeCache{}

// 并发安全的写入方法
func (c *SafeCache) Update(key, value string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	//logs.Info("update md5 map, key:%s, value:%s", key, value)
	c.data[key] = value
}

// 并发安全的读取方法(供序列化使用)
func (c *SafeCache) Clone() map[string]string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	cloned := make(map[string]string, len(c.data))
	for k, v := range c.data {
		cloned[k] = v
	}
	return cloned
}

// 并发安全的读取方法(供序列化使用)
func (c *SafeCache) GetValByKey(key string) string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	val, ok := c.data[key]
	if ok {
		return val
	}

	return ""
}

// 并发安全删除
func (c *SafeCache) Delete(key string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	for key_ := range c.data {
		if IsPathPrefixMatch(key_, key) {
			delete(c.data, key_)
		}
	}

	delete(c.data, key)
}

func (c *SafeCache) GetCacheFromConf() {
	c.mu.RLock()
	defer c.mu.RUnlock()
	c.data = GetMD5Conf()
}

var (
	writeChan = make(chan map[string]string, 1000) // 缓冲队列
	writeLock sync.Mutex
)

// 独立写入协程
func Init() {
	go func() {
		for data := range writeChan {
			doWrite(data) // 实际写入逻辑
		}
	}()
}

// 安全入口函数
func SafeSaveToMD5Conf(data map[string]string) bool {
	cloned := data // 先复制数据
	select {
	case writeChan <- cloned: // 非阻塞写入队列
	default:
		return false
	}

	return true
}

func doWrite(data map[string]string) {
	writeLock.Lock()
	defer writeLock.Unlock()

	//logs.Info("dowrite, data: %s", data)
	// 使用原子写入模式
	tmpPath := "md5_cache.json.tmp." + uuid.New().String()
	finalPath := "md5_cache.json"

	file, err := os.Create(tmpPath)
	if err != nil {
		logs.Error("创建临时文件失败: %v", err)
		return
	}
	defer os.Remove(tmpPath) // 确保异常时清理

	// 带缩进的JSON编码
	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	if err := encoder.Encode(data); err != nil {
		logs.Error("JSON编码失败: %v", err)
		file.Close()
		return
	}

	// 确保数据落盘
	if err := file.Sync(); err != nil {
		logs.Error("文件同步失败: %v", err)
		file.Close()
		return
	}
	file.Close()

	// 原子替换文件
	if err := os.Rename(tmpPath, finalPath); err != nil {
		logs.Error("文件重命名失败: %v", err)
	}
}

// 检查路径是否包含名为"conf"的目录（字符串层面）
func PathContainsConfDir(path string) bool {
	// 将路径统一为斜杠格式（兼容Windows）
	normalizedPath := filepath.ToSlash(path)
	// 拆分路径为组成部分（自动处理多个斜杠）
	parts := strings.Split(normalizedPath, "/")

	// 遍历每个路径部分
	for _, part := range parts {
		// 过滤空字符串（如路径开头/结尾的斜杠）
		if part == "" {
			continue
		}
		// 精确匹配目录名
		if part == "conf" {
			return true
		}
	}
	return false
}

// 检查路径是否包含名为"conf"的目录（字符串层面）
func PathContainsDir(path string, compare string) bool {
	// 将路径统一为斜杠格式（兼容Windows）
	normalizedPath := filepath.ToSlash(path)
	// 拆分路径为组成部分（自动处理多个斜杠）
	parts := strings.Split(normalizedPath, "/")

	// 遍历每个路径部分
	for _, part := range parts {
		// 过滤空字符串（如路径开头/结尾的斜杠）
		if part == "" {
			continue
		}
		// 精确匹配目录名
		if part == compare {
			return true
		}
	}
	return false
}

// 精确前缀匹配函数
func IsPathPrefixMatch(path, prefix string) bool {
	// 处理边界情况：当 prefix 为空时返回 false
	if len(prefix) == 0 {
		return false
	}

	// 强制添加路径分隔符检查，避免部分匹配的情况
	if !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	// 使用原生 HasPrefix 进行判断
	return strings.HasPrefix(path, prefix)
}

// 生成UUIDv4
func GenerateUUID() string {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		panic("无法生成UUID")
	}

	return fmt.Sprintf("%x-%x-%x-%x-%x",
		b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
}

// 短ID生成（示例格式：EMP2023_ABC123）
func GenerateShortID(prefix string) string {
	b := make([]byte, 3)
	rand.Read(b)
	return fmt.Sprintf("%s%X_%X", prefix, time.Now().Year(), b)
}

// 辅助函数：原子替换文件
func AtomicReplace(src, dst string) error {
	// 尝试直接重命名（Linux/MacOS可行）
	err := os.Rename(src, dst)
	if err == nil {
		return nil
	}

	// Windows特殊处理：先删除目标文件（如果存在）
	if _, err := os.Stat(dst); err == nil {
		if err := os.Remove(dst); err != nil {
			return fmt.Errorf("删除旧文件失败: %w", err)
		}
	} else if !os.IsNotExist(err) {
		return fmt.Errorf("检查文件失败: %w", err)
	}

	// 再次尝试重命名
	if err := os.Rename(src, dst); err != nil {
		return fmt.Errorf("最终重命名失败: %w", err)
	}
	return nil
}

func HandleFileUpload(machineCode, version, fullPath string) []map[string]string {
	// 解析路径层级
	pathParts := strings.Split(fullPath, "/")

	// 去除首尾元素（根据需求过滤FileList目录和文件名）
	filteredParts := []string{}
	for i, part := range pathParts {
		if i == len(pathParts)-1 { // 跳过最后文件名
			continue
		}

		filteredParts = append(filteredParts, part)
	}

	// 生成需要创建的目录路径队列
	var pathQueue []map[string]string

	currentPath := ""
	for index, part := range filteredParts {

		if index == 0 && part == "FileList" {

		} else if index == 1 && part == machineCode {

		} else if index == 2 && part == version {

		} else if index == 3 && (part == "process" || part == "conf") {

		} else {
			currentPath = strings.ReplaceAll(currentPath, "\\", "/")

			pathQueue = append(pathQueue, map[string]string{
				"parentPath": currentPath,
				"folderName": part,
			})
		}

		currentPath = filepath.Join(currentPath, part)
	}

	return pathQueue
}

// 生成安全Token（示例）
func GenerateSecureToken() string {
	// 方法1: UUIDv4
	return uuid.New().String()

	// 方法2: 加密随机字符串
	// b := make([]byte, 32)
	// if _, err := rand.Read(b); err != nil {
	//     panic(err)
	// }
	// return base64.URLEncoding.EncodeToString(b)
}

// 获取客户端信息
func GetClientInfo(ctx *context.Context) string {
	info := map[string]string{
		"ip":     ctx.Request.RemoteAddr,
		"ua":     ctx.Request.UserAgent(),
		"device": ctx.Input.Header("Device-Type"),
	}
	data, _ := json.Marshal(info)
	return string(data)
}

func ListDirectories(root string) []string {
	var dirs []string

	entries, _ := os.ReadDir(root)
	for _, entry := range entries {
		if entry.IsDir() {
			dirs = append(dirs, entry.Name()) // 仅添加当前层目录名 ‌:ml-citation{ref="5" data="citationList"}
		}
	}

	return dirs
}

func ConvertToBeijingTime(utcTime string) (string, error) {
	// 解析UTC时间（支持7位小数秒）
	t, err := time.Parse("2006-01-02T15:04:05.9999999Z", utcTime)
	if err != nil {
		return "", fmt.Errorf("时间格式错误: %v", err)
	}

	// 加载上海时区（北京时间）
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		return "", fmt.Errorf("时区加载失败: %v", err)
	}

	// 转换为北京时间并格式化输出
	return t.In(loc).Format("2006-01-02 15:04:05.000"), nil
}

func Convert2BeijingTimeFormat(timeVal time.Time) (string, error) {
	// 加载上海时区（北京时间）
	loc, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		return "", fmt.Errorf("时区加载失败: %v", err)
	}

	// 转换为北京时间并格式化输出
	return timeVal.In(loc).Format("2006-01-02 15:04:05.000"), nil
}

type FileMeta struct {
	Path string // 文件路径
	MD5  string // 文件MD5值
}

// 计算多个文件MD5的合并值
func CalculateCombinedMD5WithPath(files []FileMeta) string {
	if len(files) == 0 {
		return ""
	}

	// 按路径排序确保一致性
	sort.Slice(files, func(i, j int) bool {
		return files[i].Path < files[j].Path
	})

	var builder strings.Builder
	for _, f := range files {
		builder.WriteString(f.Path)
		builder.WriteString("|") // 添加分隔符
		builder.WriteString(f.MD5)
		builder.WriteString("\n") // 换行分隔不同文件
	}

	hash := md5.Sum([]byte(builder.String()))
	return hex.EncodeToString(hash[:])
}
